package com.gitee.l0km.beanfilter.core;

import static com.gitee.l0km.beanfilter.core.BaseFieldFilter.EMPTY_STRING_ARRAY;

import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.beanfilter.core.utils.CaseSupport;

/**
 * 对字符串类型字段输出[全部/部分]遮挡结果的值过滤器实现
 * @author guyadong
 */
public class StringCoverFilter implements IValueFilter {
	private static final String DEFAULT_COVER = "*";
	private final SimpleFieldFilter fieldFilter;
	private final boolean autoCase;
	private final String cover;
	private final int beginIndex;
	private final int endIndex;
	private final CoverString coverInstance;

	public StringCoverFilter(Class<?> beanClass, boolean autoCase,String fieldName,
			char cover, 
			Class<?>[] activeOnClasses, String[] activeOnClassNames, boolean and) {
		this(beanClass, autoCase, fieldName, cover, 0, 0, true, activeOnClasses, activeOnClassNames, and);
	}
	/**
	 * {@code com.gitee.l0km.beanfilter.core.annotations.PasswordValueFilter} 注解对应的构造方法
	 * 
	 * @param beanClass Java Bean类型
	 * @param autoCase 是否将snake-case和camel-case格式的字段字视为同一字段，如firstName和first_name,为{@code true}时视为同一个名字。
	 * @param fieldName 要过滤字段名
	 * @param cover 覆盖字符
	 * @param outputLength 输出的覆盖字符串长度,,小于等于0输出与原字符串长度一样的覆盖字符串，大于0 则视为固定长度覆盖。
	 * @param activeOnClasses 过滤器激活器类型列表，必须为{@link Activation}接口实现
	 * @param activeOnClassNames  过滤器激活器类名列表，必须为{@link Activation}接口实现类名
	 * @param and 指定多个过滤器激活器时，激活判断模式，为{@code true}为AND模式，所有激活器条件都匹配才能激活过滤器，否则为OR模式，任意一激活器条件都匹配就可以激活过滤器
	 */
	public StringCoverFilter(Class<?> beanClass, boolean autoCase,String fieldName,
			char cover, int outputLength,
			Class<?>[] activeOnClasses, String[] activeOnClassNames, boolean and) {
		this(beanClass, autoCase, fieldName, cover, 0, Math.max(0, outputLength), false, activeOnClasses, activeOnClassNames, and);
	}
	/**
	 * @see #StringCoverFilter(Class, boolean, String, String, int, int, boolean, Class[], String[], boolean)
	 */
	public StringCoverFilter(Class<?> beanClass, boolean autoCase,String fieldName,
			char coverChar,int beginIndex, int endIndex, 	boolean split, 
			Class<?>[] activeOnClasses, String[] activeOnClassNames, boolean and) {
		this(beanClass, autoCase, fieldName, new String(new char[] {coverChar}), beginIndex,endIndex, split, activeOnClasses, activeOnClassNames, and);
	}
	/**
	 * {@code com.gitee.l0km.beanfilter.core.annotations.CoverValueFilter} 注解对应的构造方法
	 * 
	 * @param beanClass Java Bean类型
	 * @param autoCase 是否将snake-case和camel-case格式的字段字视为同一字段，如firstName和first_name,为{@code true}时视为同一个名字。
	 * @param fieldName 要过滤字段名
	 * @param cover 覆盖字符
	 * @param beginIndex 0-based 覆盖起始索引,从字符串起始位置算起开始使用覆盖字符代替的索引，小于0视为0。
	 * @param endIndex 覆盖结束索引,从从字符串结尾位置(exclusive),向前的负索引值， 大于0 则视为固定长度覆盖。
	 * @param split 是否将字符串以标点符号空格（包括中文标点符号和空格）分割为单词后处理
	 * @param activeOnClasses 过滤器激活器类型列表，必须为{@link Activation}接口实现
	 * @param activeOnClassNames  过滤器激活器类名列表，必须为{@link Activation}接口实现类名
	 * @param and 指定多个过滤器激活器时，激活判断模式，为{@code true}为AND模式，所有激活器条件都匹配才能激活过滤器，否则为OR模式，任意一激活器条件都匹配就可以激活过滤器
	 */
	public StringCoverFilter(Class<?> beanClass, boolean autoCase,String fieldName,
			String cover,int beginIndex, int endIndex, boolean split, 
			Class<?>[] activeOnClasses, String[] activeOnClassNames, boolean and) {
		this.fieldFilter = new SimpleFieldFilter(beanClass,autoCase,
				true,
				EMPTY_STRING_ARRAY,
				new String[] {Strings.nullToEmpty(fieldName)},activeOnClasses, activeOnClassNames,
				and,
				EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY);
		this.autoCase = autoCase;
		this.cover = Strings.isNullOrEmpty(cover) ? DEFAULT_COVER: cover ;
		this.beginIndex = beginIndex;
		this.endIndex= endIndex;
		if(endIndex>0) {
			this.coverInstance = new FixCover(endIndex);
		}else {
			this.coverInstance = new DynamicCover(split);
		}
	}
	public Class<?> getBeanClass() {
		return fieldFilter.getBeanClass();
	}

	public String getFieldName() {
		return fieldFilter.getIncludeNames().iterator().next();
	}
	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	protected Object doProcess(Class<?> clazz,String name, Object value) {
		if(autoCase) {
			name = CaseSupport.toCamelcaseIfSnake(name);
		}
		if(value instanceof String) {
			return coverInstance.cover( (String)value, beginIndex, endIndex);
			
		}else if(null == value) {
			return cover;
		}
		return value;
	}
	@Override
	public Object process(Class<?> clazz,String name, Object value) {
		if(fieldFilter.isActive(clazz) && null != clazz && null != name && fieldFilter.permit(clazz, name)) {
			return doProcess(clazz,name,value);
		}
		return value;
	}
	@Override
	public int hashCode() {
		return Objects.hash(autoCase, beginIndex, cover, endIndex, fieldFilter);
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		StringCoverFilter other = (StringCoverFilter) obj;
		return autoCase == other.autoCase && beginIndex == other.beginIndex && Objects.equals(cover, other.cover)
				&& endIndex == other.endIndex && Objects.equals(fieldFilter, other.fieldFilter);
	}
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("StringCoverFilter [fieldFilter=").append(fieldFilter).append(", autoCase=").append(autoCase)
				.append(", cover=").append(cover).append(", beginIndex=").append(beginIndex).append(", endIndex=")
				.append(endIndex).append("]");
		return builder.toString();
	}

	private String fillCover(int length) {
		if(cover.length() == 1) {
			char[] array = new char[length];
			Arrays.fill(array, cover.charAt(0));
			return new String(array);
		}else {
			String[] array = new String[(length + cover.length()-1)/cover.length()];
			Arrays.fill(array, cover);
			return Joiner.on("").join(array).substring(0, length);
		}
	}		
	
	interface CoverString{
		String cover(String value,int beginIndex,int endIndex);
	}
	private class FixCover implements CoverString{
		private final String output;
		private FixCover(int outputLength) {
			this.output = fillCover(outputLength);
		}
		@Override
		public String cover(String value, int beginIndex, int endIndex) {
			return output;
		}
	}
	/** 英文标点符号空格及中文标点符号正则表达式 */
	private static final Pattern PUNCTUATION_PATTERN = Pattern.compile("[\\p{Punct}\\s　。 ； ， ： “ ”（ ） 、 ？ 《 》【】．]+");
	private class DynamicCover implements CoverString{
		private final boolean split;
		
		public DynamicCover(boolean split) {
			super();
			this.split = split;
		}
		private String doCover(String value, int beginIndex, int endIndex) {
			int _beginIndex = Math.max(0, beginIndex);
			_beginIndex=Math.min(_beginIndex, value.length()-1);
			int _endIndex = Math.min(value.length(), value.length() + endIndex);
			_endIndex = Math.max(_beginIndex,_endIndex);
			if (_beginIndex == _endIndex) {
				if (_beginIndex == 0) {
					if (value.length() == cover.length()) {
						return cover;
					} else {
						/** 输入长度不等于覆盖长度时，返回输入长度覆盖 */
						return fillCover(value.length());
					}
				} else {
					return value.substring(0, _beginIndex) + fillCover(1);
				}
			}
			return  new StringBuffer(value.substring(0, _beginIndex))
					.append(fillCover(_endIndex-_beginIndex))
					.append(value.substring(_endIndex))
					.toString();
		}
		public String cover(String value, int beginIndex, int endIndex) {
			if(Strings.isNullOrEmpty(value)) {
				return cover;
			}
			if(split) {
				Matcher m = PUNCTUATION_PATTERN.matcher(value); 
				StringBuffer sb = new StringBuffer();
				int lastIndex=0;
				while(m.find()){
					if(m.start()>0) {
						sb.append(doCover(value.substring(lastIndex, m.start()) ,beginIndex,endIndex));
					}
					lastIndex=m.end();
					sb.append(m.group(0));
				}
				if(lastIndex<value.length()) {
					sb.append(doCover(value.substring(lastIndex, value.length()) ,beginIndex,endIndex));
				}
				return sb.toString();
			}else {
				return doCover(value, beginIndex, endIndex);
			}
		}		
	}
}
